import {
  Component,
  OnInit,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ViewChild,
  ElementRef,
  Inject,
  AfterViewInit,
  ChangeDetectorRef,
  OnDestroy,
} from '@angular/core';
import { AvtSelectTopControlComponent } from './avt-select-top-control.component';
import { AvtSelectDataService } from './avt-select-data.service';
import { CdkConnectedOverlay } from '@angular/cdk/overlay';
import { ContentObserver } from '@angular/cdk/observers';
import { distinctUntilChanged, map } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-avt-select',
  template: `
    <avt-select-top-control
      #topControl
      cdkOverlayOrigin
      #trigger="cdkOverlayOrigin"
      [ngStyle]="{ width: width }"
      [(isOpen)]="isOpen"
      (click)="controlOpenClose($event)"
    >
    </avt-select-top-control>
    <ng-template
      cdkConnectedOverlay
      (overlayOutsideClick)="outSideClick($event)"
      [cdkConnectedOverlayOrigin]="trigger"
      [cdkConnectedOverlayOpen]="isOpen"
    >
      <avt-option-panel #optionPanel [width]="overlayMinWidth">
      </avt-option-panel>
    </ng-template>
  `,
  styles: [
    `
      :host {
        display: inline-block;
      }
    `,
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [AvtSelectDataService],
})
export class AvtSelectComponent implements OnInit, AfterViewInit, OnDestroy {
  overlayMinWidth = '240px';
  @Input() maxCountDisplay = 20;
  @Input() width = '250px';
  @Input() isOpen = false;
  @Input() mode: 'single' | 'multi' = 'multi';
  @Input() selectedValue: any[] = [];
  @Input() labelField = 'name';
  @Input() valueField = 'desc';
  @Output() selectedValueChange = new EventEmitter();
  @ViewChild('topControl', { static: true })
  topControl!: AvtSelectTopControlComponent;
  @ViewChild(CdkConnectedOverlay) cdkOverLay!: CdkConnectedOverlay;
  @Input() dataSource: any[] = [];
  
  constructor(
    private el: ElementRef,
    @Inject(AvtSelectDataService) private dataSvc: AvtSelectDataService,
    private cdr: ChangeDetectorRef,
    private observe: ContentObserver
  ) {}

  private initDataSource() {
    this.dataSource.forEach((item, index) => {
      if (!item.hasOwnProperty('index')) {
        item.index = index;
        item.__label = item[this.labelField];
        item.__value = item[this.valueField];
        item.isChecked = this.selectedValue.includes(item[this.valueField]);
      }
    });
  }

  controlOpenClose(ev: MouseEvent) {
    this.overlayMinWidth = this.topControl.elWidth;
    this.isOpen = !this.isOpen;
  }

  outSideClick(ev: any) {
    if (!this.el.nativeElement.contains(ev.target)) {
      this.isOpen = false;
    }
  }

  ngAfterViewInit() {
    const { destroy$ } = this.dataSvc;
    let obServeElement = this.topControl.element;
    this.observe
      .observe(obServeElement)
      .pipe(
        takeUntil(destroy$),
        map(() => obServeElement.getClientRects()[0].height),
        distinctUntilChanged()
      )
      .subscribe((v) => {
        this.cdkOverLay.overlayRef.updatePosition();
      });
  }

  ngOnInit(): void {
    this.initDataSource();
    this.dataSvc.dataSource$.next(this.dataSource);
    this.dataSvc.init({
      mode: this.mode,
      labelField: this.labelField,
      valueField: this.valueField,
      selectedValue: this.selectedValue,
      maxCountDisplay: this.maxCountDisplay,
    });
  }

  ngOnDestroy() {
    const { destroy$ } = this.dataSvc;
    destroy$.complete();
    destroy$.next();
  }
}
